#COMBAT MODELS
import random
import math

import django.dispatch
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db import models

from game.monster.models import group, individual
import game.ai.signals
from game.ai.models import AVAILABLE_CONTROLLERS
from game.common.logger import logger
from game.combat import events, combatDispatcher 


COMBAT_STATES = (
				 (1, "PROCESSING"),
				 (2, "WAITING FOR ACTION"),
				 (3, "OVER")
				 )
#Signal to declare that the combat is waiting for an action, calls the AI to pick a combat request
              
combat_waiting_for_action = django.dispatch.Signal(providing_args=["combatant"])
combat_signal = django.dispatch.Signal(providing_args=["event"])

combat_waiting_for_action.connect(game.ai.signals.getActionRequest)
combat_signal.connect(combatDispatcher.dispatchCombatSignal)

class combat(models.Model):
	"""Contains the information for an active combat situation """
	#messages = models.TextField(blank = True, null=True)
	currentState = models.IntegerField(default = 1, choices = COMBAT_STATES)
	currentCombatant = models.ForeignKey('combat.combatant', blank=True, null = True, default = None)
	isActive = models.BooleanField(default = True)     #Flag that combat is still on going
	permaDeath = models.BooleanField(default = False)  #Flag if creature dies or get knocked out
	captureAllowed = models.BooleanField(default = True)   #flag if creatures can be captured, not wanted for most PvP
	maxWaitTime = models.IntegerField(default = -1)    #Time to wait for a play to act, -1 is unlimited
	sideA = models.ForeignKey('combat.combatSide', related_name ="combatSideA")
	sideB = models.ForeignKey('combat.combatSide', related_name ="combatSideB")
	losingSide = models.ForeignKey('combat.combatSide', related_name="losingSide", blank=True, null = True, default = None)
	ticksPerTurn = 1  #number of speed ticks to deduct while a combatant is waiting to act
	
	
	class Meta():
		abstract = False
		
	def addIndividualToCombat(self, ind, side, control=0, ticks=10):
		try:
			logger.debug("Generating combatant. Individual: %s Side: %s"%(type(ind), side.id))
			cmbt = combatant(individual=ind, side = side, currentCombat = self, controller=control, speedTicks=ticks)
			cmbt.save()
			return cmbt
		except Exception, e:
			logger.error("ERROR: Couldn't add individual to combat: %s"%(e))	
			raise e
		
	def allCombatants(self):
		"""Returns a set that contains all the monsters in the fight"""
		monsters = []
		monsters = combatant.objects.filter(currentCombat = self)
		return monsters
	
	def getNextCombatant(self):
		"""iterates through the monsters and ticks time off until a monster is"""
		"""ready to act"""
		if self.combatOver():
			self.endCombat()
			return False
		combatants = self.allCombatants()
		try:
			#start counting from last actor to act
			startLocation = combatants.index(self.currentCombatant)
		except:
			#Actor is no longer in combat, start from begining
			startLocation = 0
		set = False
		while(set == False):
			#Keep looping until someone can act
			#If at the end of the list, start over
			if startLocation+1 >= len(combatants):
				startLocation=0
			else:
				startLocation+=1
			#check monster ready to act
			cmbt = combatants[startLocation]
			if not cmbt.individual.isAlive():
			#individual is dead, move along
				pass
			elif cmbt.speedTicks==0:
				logger.debug("It is %s turn to act" % cmbt.individual.name)
				self.currentCombatant=cmbt
				self.currentState = 2 #state to accept actions
				self.save()
				set=True
			#not ready, deduct timeout and move on
			else:
				#monster is waiting
				logger.debug("%s is waiting. %s more turns" % (cmbt.individual.name, cmbt.speedTicks) )
				combatants[startLocation].deductTick(self.ticksPerTurn)
		#The current combatant is now set. signal that combat is waiting for an action to be performed
		combat_signal.send(sender=self, event = events.CombatantReadyToActEvent(self.currentCombatant)) 
		combat_waiting_for_action.send(sender=self, combatant=self.currentCombatant)		
		return self.currentCombatant	
	
	def endCombat(self):
		#wraps up the end of combat
		self.isActive = False
		self.addMessage("Combat is over")
		if self.losingSide is not self.sideA:
			self.addMessage("You have won!")
		else:
			self.addMessage("You have lost")
		self.save()
	
	def tearDown(self):
		logger.debug("Tearing down combat: %s"%(self.id))
	#delete the combat instance and associated models
		for x in self.sideB.allMonsters():
		#destroy enemy models
			x.individual.delete()
		self.sideB.delete()
		self.sideA.delete()
		self.delete()
	def startCombat(self):
		pass
	def combatOver(self):
		#checks to see if combat should continue or should end
		logger.debug("Checking to see if combat is over")
		for x in self.sideA,self.sideB:
			mon = x.allMonsters()
			shouldContinue = 0
			for y in mon:
				if y.individual.isAlive():
					shouldContinue = 1
					
			if shouldContinue:
				continue
			else:
				logger.debug("Combat is over")
				self.currentState = 3 #Over
				self.losingSide = x
				self.save()
				combat_signal.send(sender = self, event = events.CombatFinishedEvent())
				return True
		 
		return False	
		
	def addMessage(self, message):
		# Takes a string message and adds it to messages for combat
		#logger.debug("Adding message: ", str(message))
		newMessage = combatMessage(combat = self, message = message)
		newMessage.save()
		return newMessage.id
		
	def initializeAction(self):
		if self.currentState != 1:
			self.currentState = 1
			self.save()
			logger.debug("Starting to process action")
			return True
		return False
	
	def validateRequest(self, req):
		if req.caller != self.currentCombatant:
			raise InvalidCombatantException, "%s is not current. %s is acting" % (req.caller, self.currentCombatant)
			return False
		if not req.caller.individual.hasAction(req.action):
			raise InvalidActionException
			return False
		if req.caller.individual.mpLeft < req.action.mpNeeded:
			raise NotEnoughMPException
			return False
		logger.debug("ActionRequest validated successfuly")
		return True
	
	def processRequest(self, req):
		logger.debug("Action received: %s is doing %s to %s" % (req.caller.individual.name, req.action.name, req.target.individual.name))
		try: 
			self.initializeAction() 
			self.validateRequest(req)
			req.caller.addTick(req.action.speed)
			logger.debug("Attempting action")
			message = req.getAttemptMessage()
			self.addMessage(message)
			
			if not req.getSuccess():
				logger.debug("Action Failed")
				message = req.onFailure()
				self.addMessage(message)
			else:
				logger.debug("Action Succeeded")
				message = req.onSuccess()
				self.addMessage(message)
			
			self.currentState = 2
			self.save()
			self.getNextCombatant()
		except Exception, e:
			logger.debug("Action failed validation")
			logger.debug(e)		
		
class combatant(models.Model):
	individual = models.ForeignKey('monster.individual', unique = True)
	currentCombat = models.ForeignKey('combat.combat')
	side = models.ForeignKey('combat.combatSide')
	speedTicks = models.IntegerField(default = 0)
	controller = models.IntegerField(default = 1, choices = AVAILABLE_CONTROLLERS)
	
	def __unicode__(self):
		return self.individual.name
		
	def getAction(self):
		return self.controler.getAction()
	
	def deductTick(self, ticks):
		self.speedTicks -=ticks
		if self.speedTicks < 0:
			self.speedTicks = 0
		self.save()
	
	def addTick(self, ticks):
		self.speedTicks += ticks
		logger.debug("Adding %s ticks"%(ticks))
		self.save()
	
	def getAllies(self):
		#Returns a set of combatants that are on the same side
		allies = []
		for x in self.currentCombat.allCombatants():
			if x.individual.group == self.individual.group:
				allies.append(x)
		return allies
	
	def getEnemies(self):
		#Returns a set of combatants on the other side
		enemies = []
		for x in self.currentCombat.allCombatants():
			if x.individual.group != self.individual.group:
				enemies.append(x)
		return enemies	
	
class combatSide(models.Model):
	class Meta():
		abstract = False
		
	def allMonsters(self):
		return combatant.objects.filter(side=self)

class combatMessage(models.Model):
	combat = models.ForeignKey('combat.combat')
	message = models.CharField(max_length=255)
	
	class Meta:
		ordering = ['id']

	
class InvalidCombatantException(Exception):
	pass

class NotEnoughMPException(Exception):
	pass

class InvalidActionException(Exception):
	pass

